#include <iostream>
#include <algorithm>
#include <functional>

#include <boost/foreach.hpp>
#include <boost/bind.hpp>

#include <unistd.h>
#include <fcntl.h>

#include "vector_space_indexer.h"
#include "page_vector.h"
#include "junix.h"
#include "idxutils.h"

namespace jsearch
{

struct page_vector::Impl
{
	public:
		Impl( std::string db_name )
			:disk_db(db_name)
		{ }

		bool restore( std::tr1::shared_ptr<std::vector<std::string> >& svec)
		{
			using boost::bind;
			for_each( svec->begin(),
					  svec->end(),
				   	  bind(&Impl::load_term, this, _1) );

			return true;
		}

		std::set<std::string> get_term_set()
		{
			std::set<std::string> tset;

			BOOST_FOREACH(INDEX_V& iv,index) {
				tset.insert(iv.first);
			}

			return tset;
		}


		std::set<int> get_page_id_set()
		{
			std::set<int> pset;

			BOOST_FOREACH(INDEX_V& iv,index) {
				BOOST_FOREACH(item& i,iv.second.posting_list) {
					pset.insert(i.page_id);
				}
			}

			return pset;
		}

		double get_weight(std::string tm, int pid)
		{

			std::string term = str_tolower(tm);

			INDEX::iterator pos;

			if ( (pos=index.find(term)) != index.end()) {
				using boost::bind;
				std::vector<item>& pl = pos->second.posting_list;

				std::vector<item>::iterator iter = 
					find_if( pl.begin(),
							 pl.end(),
							 bind(std::equal_to<int>(),
								bind(&item::page_id, _1),  pid));

				if (iter!=pl.end())
					return iter->weight;
			}

			return 0.0;
		}


		double get_idf(std::string tm)
		{
			std::string term = str_tolower(tm);

			INDEX::iterator pos;
			if ((pos=index.find(term))!=index.end())
				return pos->second.idf;

			return 0.0;
		}


	private:

		bool load_term(std::string tm)
		{
			std::string term=str_tolower(tm);

			if (term.empty())
				return true;

			int fd =open(disk_db.c_str(),O_RDONLY);
			if (fd==-1)
				err_sys("db file %s open error",disk_db.c_str());

			int header_zone;
			readn( fd, (char*)&header_zone, sizeof(int) );
			if (header_zone<=4)	{
				std::cerr<<"db file header is damaged"
					<<std::endl;
				exit(1);
			}

			disk_index_header dheader;

			int header_size  = sizeof(disk_index_header);
			int data_size  = sizeof(disk_index_data);

			bool found = false;

			int low=sizeof(int);
			int hign=header_zone;
			int length = (hign-low)/header_size;

			Lseek(fd, sizeof(int), SEEK_SET );
			for (int i=0;i<length;i++) {
				readn(fd, (char*)&dheader, header_size);
				std::string cadidate = std::string(dheader.name);
				if (term == cadidate) {
					found = true;
					break;
				}
			}


			/*
			   int mid=low+(((hign-low)/header_size)/2)*header_size;

			   while(hign-low > header_size)
			   {
			   Lseek(fd,mid, SEEK_SET);
			   readn(fd, (char*)&dheader, header_size);

			   std::string cadidate = string(dheader.name);

			   if( term == cadidate)
			   {
			   found = true;
			   break;
			   }
			   else if (term < cadidate)
			   {
			   hign = mid;
			   mid=low+(((hign-low)/header_size)/2)*header_size;
			   }else{
			   low = mid;
			   mid=low+(((hign-low)/header_size)/2)*header_size;
			   }
			   }
			   */

			if (found) {
				raw_posting_list &entry = index[term];
				entry.idf = dheader.idf;
				disk_index_data *buf = (disk_index_data*)alloca( dheader.size);
				Lseek(fd,dheader.offset,SEEK_SET);
				readn(fd, (char*)buf, dheader.size); 

				for (int i=0; i<dheader.size/data_size; i++) {
					entry.posting_list.push_back( item( buf[i].page_id,
								buf[i].weight));	
				}
			}

			return found;
		}

	private:
		INDEX index;
		std::string disk_db;
};


//============================================================
// member function for page_vector
//============================================================

page_vector::page_vector( std::string db_name )
	:pimpl(new Impl(db_name))
{ }


bool page_vector::restore(std::tr1::shared_ptr<std::vector<std::string> >& svec)
{
	return pimpl->restore(svec);
}

double page_vector::get_weight(std::string tm, int pid)
{
	return pimpl->get_weight(tm, pid);
}

double page_vector::get_idf(std::string tm)
{
	return pimpl->get_idf(tm);
}

std::set<std::string> page_vector::get_term_set()
{
	return pimpl->get_term_set();
}

std::set<int> page_vector::get_page_id_set()
{
	return pimpl->get_page_id_set();
}


}//end namespace
